package com.transaction.simulate.util;

import com.transaction.common.util.ConstUtil;
import com.transaction.common.util.DataUtil;
import com.transaction.common.util.FileUtil;
import com.transaction.common.util.ParamsUtil;
import com.transaction.simulate.mapper.ChannelMapper;
import com.transaction.simulate.model.dto.Channel;
import com.transaction.simulate.model.dto.Interface;
import com.transaction.simulate.service.*;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.bcel.Const;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 组装文件
 * @Title:
 * @author: dy.yin 2021/8/20 11:02
 */

@Component
public class AssembleFileUtil {

    private static Logger LOGGER = LoggerFactory.getLogger(AssembleFileUtil.class);

    @Autowired
    private InterfaceService interfaceServiceBean;
    private static InterfaceService interfaceService;

    @Autowired
    private AccountService accountServiceBean;
    private static AccountService accountService;

    @Autowired
    private TransService transServiceBean;
    private static TransService transService;

    @Autowired
    private ChannelMapper channelMapperBean;
    private static ChannelMapper channelMapper;

    @Autowired
    private FileService fileServiceBean;
    private static FileService fileService;

    @Autowired
    private ElContractService elContractServiceBean;
    private static ElContractService elContractService;



    //初始化注入bean
    @PostConstruct
    public void init(){
        interfaceService = interfaceServiceBean;
        accountService = accountServiceBean;
        channelMapper = channelMapperBean;
        fileService = fileServiceBean;
        transService = transServiceBean;
        elContractService = elContractServiceBean;
    }

    //获得文件字段
    public static String generateFileFields(String fileType,String versionCode){
        LOGGER.info("获得文件{}的字段信息",fileType);
        StringBuilder retValue = new StringBuilder();
        String interfaceCode = fileType + versionCode;
        List<Interface> interfaceList = interfaceService.selectInterfaceByCode(interfaceCode);
        retValue.append(DataUtil.prefixReplenishZero(String.valueOf(interfaceList.size()),3))
                .append(ConstUtil.LINE_FEED);
        interfaceList.forEach(v ->{
            retValue.append(v.getColEnName().toUpperCase())
                    .append(ConstUtil.LINE_FEED);
        });
        return retValue.toString();
    }


    //生成索引文件
    public static void generateIndexFile(String fileDir, String channelCode, String transDate) {
        LOGGER.info("生成渠道{},日期{}的索引文件",channelCode,transDate);
        List<String> fileNames = new ArrayList<>();
        List<String> elFileNames = new ArrayList<>();
        List<String> fxqFileNames = new ArrayList<>();

        Channel channel = channelMapper.selectChannelByChannelCode(channelCode);

        File[] files = new File(fileDir).listFiles();
        for(File file : files){
            String fileName = file.getName();

            if(fileName.contains(ConstUtil.UNDER_LINE+ConstUtil.FILE_TYPE_01+ConstUtil.FILE_SUFFIX)
                    || fileName.contains(ConstUtil.UNDER_LINE+ConstUtil.FILE_TYPE_03+ConstUtil.FILE_SUFFIX)
                    || fileName.contains(ConstUtil.UNDER_LINE+ConstUtil.FILE_TYPE_R1+ConstUtil.FILE_SUFFIX)
            ){
                fileNames.add(file.getName());
            }

            if(fileName.contains(ConstUtil.UNDER_LINE+ConstUtil.FILE_TYPE_43+ConstUtil.FILE_SUFFIX) || fileName.contains(ConstUtil.UNDER_LINE+ConstUtil.FILE_TYPE_31+ConstUtil.FILE_SUFFIX)){
                elFileNames.add(file.getName());
            }
            if(fileName.contains(ConstUtil.UNDER_LINE+ConstUtil.FILE_TYPE_X1+ConstUtil.FILE_SUFFIX) || fileName.contains(ConstUtil.UNDER_LINE+ConstUtil.FILE_TYPE_X3+ConstUtil.FILE_SUFFIX)){
                fxqFileNames.add(file.getName());
            }

        }

        if(CollectionUtils.isNotEmpty(fileNames)){
            buildIndexContextAndWrite(channel,transDate,fileNames,fileDir,ConstUtil.INDEX_FILE_PREFIX);
        }
        if(CollectionUtils.isNotEmpty(elFileNames)){
            buildIndexContextAndWrite(channel,transDate,elFileNames,fileDir,ConstUtil.EL_INDEX_FILE_PREFIX);
        }
        if(CollectionUtils.isNotEmpty(fxqFileNames)){
            buildIndexContextAndWrite(channel,transDate,fxqFileNames,fileDir,ConstUtil.FXQ_INDEX_FILE_PREFIX);
        }
    }

    //组装索引文件内容并写出
    public static void buildIndexContextAndWrite(Channel channel, String transDate,
                                                 List<String> fileNames, String fileDir,String prefix) {
        String indexContext = generateIndexesFileContext(channel,transDate,fileNames);
        File indexFile = new File(fileDir + generateIndexFileName(channel,transDate,prefix));
        FileUtil.writeFile(indexFile,indexContext);
    }


    //索引文件内容
    public static String generateIndexesFileContext(Channel channel,String transDate,List<String> fileNames){
        StringBuilder retValue = new StringBuilder();

        String versionCode = channel.getVersionCode();
        String creatorCode = channel.getCreatorCode();
        String receiverCode = channel.getReceiverCode();
        int length = 3;
        if("22".equals(versionCode)){
            length = 8;
        }

        retValue.append(ConstUtil.INDEX_FILE_FIRST_LINE).append(ConstUtil.LINE_FEED)
                .append(versionCode).append(ConstUtil.LINE_FEED)
                .append(creatorCode).append(ConstUtil.LINE_FEED)
                .append(receiverCode).append(ConstUtil.LINE_FEED)
                .append(transDate).append(ConstUtil.LINE_FEED)
                .append(DataUtil.prefixReplenishZero(String.valueOf(fileNames.size()),length)).append(ConstUtil.LINE_FEED)
                .append(String.join(ConstUtil.LINE_FEED,fileNames)).append(ConstUtil.LINE_FEED)
                .append(ConstUtil.FILE_LAST_LINE);

        return retValue.toString();
    }

    /**
     * 生成文件名称
     * @Title: generateFileName
     * @author: dy.yin 2021/8/20 17:40
     */
    public static String generateFileName(Channel channel, String fileType, String transDate) {
        LOGGER.info("生成渠道{},日期{},文件的文件名",channel.toString(),transDate,fileType);
        StringBuilder retValue = new StringBuilder();

        String creatorCode = channel.getCreatorCode();
        String receiverCode = channel.getReceiverCode();

        retValue.append(ConstUtil.FILE_PREFIX)
                .append(creatorCode).append(ConstUtil.UNDER_LINE)
                .append(receiverCode).append(ConstUtil.UNDER_LINE)
                .append(transDate).append(ConstUtil.UNDER_LINE)
                .append(fileType).append(ConstUtil.FILE_SUFFIX);
        return retValue.toString();
    }

    /**
     * 索引文件名称
     * OFI_xxx_??_yyyymmdd.TXT
     * @Title: generateIndexFileName
     * @author: dy.yin 2021/9/8 8:57
     */
    public static String generateIndexFileName(Channel channel,String transDate,String prefix) {
        LOGGER.info("生成渠道{},日期{},文件的索引文件名",channel.toString(),transDate);
        StringBuilder retValue = new StringBuilder();

        String creatorCode = channel.getCreatorCode();
        String receiverCode = channel.getReceiverCode();

        retValue.append(prefix)
                .append(creatorCode).append(ConstUtil.UNDER_LINE)
                .append(receiverCode).append(ConstUtil.UNDER_LINE)
                .append(transDate).append(ConstUtil.FILE_SUFFIX);
        return retValue.toString();
    }


    /**
     * 获得整个文件的内容
     * @return
     */
    public static String getFileContext(Channel channel,String fileType,String transDate,String data){
        StringBuilder retValue = new StringBuilder();

        String versionCode = channel.getVersionCode();
        String top = FileUtil.buildFileTop(channel.getCreatorCode(),channel.getReceiverCode(),versionCode,transDate,fileType);
        String fields = generateFileFields(fileType,versionCode);

        retValue.append(top)
                .append(fields)
                .append(data)
                .append(ConstUtil.FILE_LAST_LINE);
        return retValue.toString();
    }

    //组装文件数据
    public static String buildFileData(String fileType, String versionCode, List<Map<String,Object>> data){
        LOGGER.info("组装文件{}的文件内容",fileType);
        StringBuilder retValue = new StringBuilder();
        int length = 8;
        if("22".equals(versionCode)){
            length = 16;
        }
        retValue.append(DataUtil.prefixReplenishZero(String.valueOf(data.size()),length))
                .append(ConstUtil.LINE_FEED);
        if(CollectionUtils.isNotEmpty(data)) {
            String interfaceCode = fileType + versionCode;
            List<Interface> interfaceList = interfaceService.selectInterfaceByCode(interfaceCode);

            data.forEach(line -> {
                interfaceList.forEach(v -> {
                    String colType = v.getColType();
                    Integer colLength = v.getColLength();
                    Integer colDecimal = v.getColDecimal();
                    String defaultValue = v.getDefaultValue();
                    String colEnName = v.getColEnName().toUpperCase();

                    String value = MapUtils.getString(line, colEnName, defaultValue);
                    //N类型前补0
                    if (ConstUtil.COLE_TYPE_N.equals(colType)) {
                        //有小数位先后补0
                        if (colDecimal != 0) {
                            value = DataUtil.fillingDecimalPlaces(value,colDecimal);
                        }
                        value = DataUtil.prefixReplenishZero(value, colLength);
                    } else if (ConstUtil.COLE_TYPE_A.equals(colType) || ConstUtil.COLE_TYPE_C.equals(colType)) {//A C类型后补空格
                        value = DataUtil.suffixReplenishSpace(value,colLength);
                    }
                    retValue.append(value);
                });
                retValue.append(ConstUtil.LINE_FEED);
            });
        }
        return retValue.toString();
    }

    //生成文件
    public static void generateChannelFile(String channelCode,
                                           String transDate,
                                           List<String> dateRange,
                                           String fileType) {
        LOGGER.info("生成渠道{}，日期{}，数据日期范围{}，文件类型{}的文件",channelCode,transDate,dateRange.toString(),fileType);
        //查询文件数据
        List<Map<String,Object>> data = getFileData(channelCode,dateRange,fileType);
        Channel channel = channelMapper.selectChannelByChannelCode(channelCode);
        String fileData = buildFileData(fileType,channel.getVersionCode(),data);
        //组装文件数据
        String fileContext = getFileContext(channel,fileType,transDate,fileData);
        //生成文件名称
        String fileName = generateFileName(channel,fileType,transDate);
        String filePath = FileUtil.buildSendFilePath(channelCode,transDate,ConstUtil.TRANSACTION_SIMULATE);
        //写文件
        FileUtil.writeFile(new File(filePath + fileName),fileContext);
    }

    //获得文件数据
    private static List<Map<String, Object>> getFileData(String channelCode,
                                                         List<String> dateRange,
                                                         String fileType) {
        LOGGER.info("查询渠道{}，日期范围{}，文件类型{}的文件数据",channelCode,dateRange.toString(),fileType);
        List<Map<String, Object>> data = new ArrayList<>();
        switch (fileType){
            case ConstUtil.FILE_TYPE_01 :
                data = accountService.getAccountToMap(channelCode,dateRange);
                break;
            case ConstUtil.FILE_TYPE_03 :
                data = transService.getTransToMap(channelCode,dateRange);
                break;
            case ConstUtil.FILE_TYPE_43 :
                data = elContractService.getElContractToMap(channelCode,dateRange);
                break;
            case ConstUtil.FILE_TYPE_31 :
                data = elContractService.get31ElContractToMap(channelCode,dateRange);
                break;
            case ConstUtil.FILE_TYPE_R1 :
                data = accountService.getAccountR1ToMap(channelCode,dateRange);
                break;
            case ConstUtil.FILE_TYPE_X1 :
                data = accountService.getAccountX1ToMap(channelCode,dateRange);
                break;
            case ConstUtil.FILE_TYPE_X3 :
                data = accountService.getAccountX3ToMap(channelCode,dateRange);
                break;
        }
        //驼峰键值统一转大写
        data = exchangeMapKeyToUpper(data);
        return data;
    }

    //键值转大写
    private static List<Map<String, Object>> exchangeMapKeyToUpper(List<Map<String, Object>> data) {
        List<Map<String, Object>> retValue = new ArrayList<>();
        data.forEach(v->{
            Map<String, Object> retMap = new HashMap<>();
            for (String key : v.keySet()) {
                retMap.put(key.toUpperCase(),v.get(key));
            }
            retValue.add(retMap);
        });
        return retValue;
    }

    //解析文件
    public static String analysisFile(String channelCode, String transDate,
                                    String fileName,File file){
        Channel channel = channelMapper.selectChannelByChannelCode(channelCode);
        String versionCode = channel.getVersionCode();
        String retMsg = null;

        //正式文件
        if(fileName.indexOf(ConstUtil.FILE_PREFIX) != -1){
            LOGGER.info("开始解析文件：{}",fileName);
            String[] nameSplit = fileName.split(ConstUtil.UNDER_LINE);
            String fileType = nameSplit[4].substring(0,2);
            LOGGER.info("文件类型:{}",fileType);

            String interfaceCode = fileType + versionCode;
            List<Interface> interfaceList = interfaceService.selectInterfaceByCode(interfaceCode);

            Map<String,Object> retMap = readFormalFile(channel,file,transDate,interfaceList);
            retMsg = MapUtils.getString(retMap,"retMsg");
            if(StringUtils.isEmpty(retMsg)){
                List<Map<String,Object>> dataList = (List<Map<String, Object>>) MapUtils.getObject(retMap,"dataList");
                if(CollectionUtils.isNotEmpty(dataList)){
                    fileService.handleAnalysisFileData(channelCode,transDate,fileType,dataList);
                }
                String newFileName = file.getPath() + ".bak";
                file.renameTo(new File(newFileName));
                LOGGER.info("文件:{}解析完成,修改为:{}",fileName,newFileName);
            }
        }
        //TODO 索引文件
        if(fileName.startsWith(ConstUtil.INDEX_FILE_PREFIX)
                || fileName.startsWith(ConstUtil.EL_INDEX_FILE_PREFIX)
                || fileName.startsWith(ConstUtil.HQ_INDEX_FILE_PREFIX)
                || fileName.startsWith(ConstUtil.FXQ_INDEX_FILE_PREFIX) ){
            return fileName + "为索引文件,暂不支持解析该文件!";
        }
        return StringUtils.isEmpty(retMsg) ? "解析文件:"+fileName+"成功" : retMsg;

    }

    //解析文件
    public static Map<String,Object> readFormalFile(Channel channel, File file, String transDate,List<Interface> interfaceList) {
        Map<String,Object> retMap = new HashMap<>();
        StringBuilder retMsg = new StringBuilder();
        List<Map<String,Object>> dataList = new ArrayList<>();
        InputStreamReader isr = null;
        BufferedReader reader = null;
        try {
            isr = new InputStreamReader(new FileInputStream(file), ConstUtil.FILE_ENCODING);
            reader = new BufferedReader(isr);
            String context = "";
            int line = 1;
            int fieldNum = 0;
            int dataNum = 0;

            String versionCode = channel.getVersionCode();
            String receiverCode = channel.getReceiverCode();
            String creatorCode = channel.getCreatorCode();

            String fileName = file.getName();
            String fileType = fileName.substring(fileName.lastIndexOf("_")).replace(ConstUtil.FILE_SUFFIX,"");

            //数据行长度
            int length = interfaceList.stream().collect(Collectors.summingInt(Interface::getColLength));

            //开始读取
            while((context = reader.readLine()) != null){
                //第1行文件开头
                if(line == 1){
                    if(!ConstUtil.FILE_FIRST_LINE.equals(context.trim())){
                        LOGGER.info("文件首行错误,首行内容为{}，应该为{}",context,ConstUtil.FILE_FIRST_LINE);
                        retMsg.append("文件:").append(fileName).append("首行错误,首行内容为:").append(context).append(",应该为:").append(ConstUtil.FILE_FIRST_LINE).append(ConstUtil.LINE_FEED);
                    }
                }
                //第2行版本号
                if(line == 2){
                    if(!versionCode.equals(context.trim())){
                        LOGGER.info("版本号错误,内容为{}，应该为{}",context,versionCode);
                        retMsg.append("文件:").append(fileName).append("版本号错误错误,内容为:").append(context).append(",应该为:").append(versionCode).append(ConstUtil.LINE_FEED);
                    }
                }
                //第3行接收人编码
                if(line == 3){

                    if(ConstUtil.RECEIVE_FILE_TYPE.contains(fileType)){
                        if(!receiverCode.equals(context.trim())){
                            LOGGER.info("接收人编码错误,内容为{}，应该为{}",context,receiverCode);
                            retMsg.append("文件:").append(fileName).append("接收人编码错误,内容为:").append(context).append(",应该为:").append(receiverCode).append(ConstUtil.LINE_FEED);
                        }
                    }else if(ConstUtil.SEND_FILE_TYPE.contains(fileType)){
                        if(!creatorCode.equals(context.trim())){
                            LOGGER.info("接收人编码错误,内容为{}，应该为{}",context,creatorCode);
                            retMsg.append("文件:").append(fileName).append("接收人编码错误,内容为:").append(context).append(",应该为:").append(creatorCode).append(ConstUtil.LINE_FEED);
                        }
                    }
                }
                //第4行发送人编码
                if(line == 4){
                    if(ConstUtil.RECEIVE_FILE_TYPE.contains(fileType)){
                        if(!creatorCode.equals(context.trim())){
                            LOGGER.info("发送人编码错误,内容为{}，应该为{}",context,creatorCode);
                            retMsg.append("文件:").append(fileName).append("发送人编码错误,内容为:").append(context).append(",应该为:").append(creatorCode).append(ConstUtil.LINE_FEED);
                        }
                    }else if(ConstUtil.SEND_FILE_TYPE.contains(fileType)){
                        if(!receiverCode.equals(context.trim())){
                            LOGGER.info("发送人编码错误,内容为{}，应该为{}",context,receiverCode);
                            retMsg.append("文件:").append(fileName).append("发送人编码错误,内容为:").append(context).append(",应该为:").append(receiverCode).append(ConstUtil.LINE_FEED);
                        }
                    }
                }
                //第5行日期
                if(line == 5){
                    if(!transDate.equals(context.trim())){
                        LOGGER.info("日期错误,内容为{}，应该为{}",context,transDate);
                        retMsg.append("文件:").append(fileName).append("日期错误,内容为:").append(context).append(",应该为:").append(transDate).append(ConstUtil.LINE_FEED);
                    }
                }
                //第10行字段数
                if(line == 10){
                    fieldNum = Integer.valueOf(context.trim());
                }
                //第11行到 10+第10行数值  字段值

                //11+第10行数值 数据行数
                if(line == (11 + fieldNum)){
                    dataNum = Integer.valueOf(context.trim());
                }

                //数据行
                if(line > 11 + fieldNum && line <= (11 + fieldNum + dataNum)){
                    Map<String,Object> map = new HashMap<>();
                    LOGGER.info("开始读取数据行：{}",context);

                    if(length != context.getBytes(ConstUtil.FILE_ENCODING).length){
                        LOGGER.info("文件第:{}行,数据行与接口字段长度和不一致,接口长度和为：{},数据行长度为:{}",line,length,context.length());
                        retMsg.append("文件:").append(fileName).append("第:").append(line).append(",数据行与接口字段长度和不一致,接口长度和为:").append(length)
                                .append("数据行长度为:").append(context.length()).append(ConstUtil.LINE_FEED);
                        line++;
                        continue;
                    }
                    int index = 0;
                    byte[] data = context.getBytes(ConstUtil.FILE_ENCODING);
                    for (Interface v : interfaceList) {
                        int colLength = v.getColLength();
                        int colDecimal = v.getColDecimal();
                        String colEnName = v.getColEnName();
                        String colType = v.getColType();

                        byte[] value = new byte[colLength];

                        System.arraycopy(data, index,value,0,colLength);
                        String realValue = null;
                        try {
                            realValue = new String(value,ConstUtil.FILE_ENCODING).trim();
                        } catch (Exception e) {
                            LOGGER.error(ParamsUtil.getErrorMsg(e));
                        }

                        if(colDecimal != 0 ){
                            realValue = realValue.substring(0,colLength-colDecimal)
                                    + "."
                                    + realValue.substring(colLength-colDecimal);
                        }

                        if(ConstUtil.COLE_TYPE_N.equals(colType)){
                            realValue = new BigDecimal(realValue).stripTrailingZeros().toPlainString();
                        }

                        map.put(colEnName.toUpperCase(),realValue);
                        index = index + colLength;
                    }
                    dataList.add(map);
                }

                line++;
            }
            LOGGER.info("解析的数据：{}",dataList);
            retMap.put("dataList",dataList);
            retMap.put("retMsg",retMsg);
            return retMap;
        }catch(Exception e){
            LOGGER.error(ParamsUtil.getErrorMsg(e));
        }finally {
            try {
                if(reader != null){
                    reader.close();
                }
                if(isr != null){
                    isr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return retMap;
    }
}
